//-------
// IMPLEMENTATION
//-------------------
#include <math.h>
#include "player.h"
#include <stdio.h>

Player *Player::m_instance = NULL;

Player* Player::getInstance(){
	if(!m_instance){
		m_instance = new Player();
	}
	return m_instance;
}


Player::Player() {
	//Game= game::getInstance();
	//AKS
	//Player Position harcoded and is needed by map class to know
	//where to draw starting view of map
	//TODO:  Get a better method to do this.
	position.x = 0.0;
	position.y = 208.0;
	prevPosition.x =0.0;
	prevPosition.y =0.0;
	velocity.x = 0.0;
	velocity.y = 0.0;
	playerOffset.x = 0.0;
	playerOffset.y = 0.0;

	m_maxSpeedX = 2.0;
	m_maxSpeedY = 7.0;
	m_runSpeed = 0.5;
	m_jumpSpeed = 5.5;//was 6.5
	m_friction = .8;
	m_jumpCount = 0;
	m_prevMaxSpeedX=m_maxSpeedX;
	m_prevMaxSpeedY=m_maxSpeedY;
	m_prevRunSpeed=m_runSpeed;
	m_prevJumpSpeed=m_jumpSpeed;
	m_prevFriction = m_friction;
	m_gravityConstant = 0.25;
	m_facingRight = false;
	m_inAir = true;
	m_attacking = false;
	m_icy=false;
	m_attackDirection=0;

	setPrevBounds();
	setBounds();


	frames = 0;
	counter = 0;
	m_speedUpTimer=0;

	//Weapon pointer
	//m_curWeapon=NULL;
	//m_secondWeapon=NULL;
	m_secondaryAmmo = 10;

	m_health = 120;

	//Needed for sprites
	m_sprite = lnSpriteCreate(SCREEN_MAIN, SPRSIZE_32x32, COLMODE_16, megaman3Tiles, megaman3Pal);
	m_spriteAnim = lnSpriteAnimBind(m_sprite, megaman3Tiles, 1, 7);

	m_healthBarSprite = lnSpriteCreate(SCREEN_MAIN, SPRSIZE_32x32, COLMODE_16, lifebarTiles, lifebarPal);
	m_healthBarSpriteAnim = lnSpriteAnimBind(m_healthBarSprite,lifebarTiles,1,7);
	m_healthBarSprite->isHidden=false;
	m_healthBarSprite->posX=0;
	m_healthBarSprite->posY=0;

	m_ammoCounter = lnSpriteCreate(SCREEN_MAIN,SPRSIZE_32x32,COLMODE_16,ammoCounterTiles,ammoCounterPal);
	m_ammoCounterAnim = lnSpriteAnimBind(m_ammoCounter,ammoCounterTiles,1,7);
	m_ammoCounter->isHidden=false;
	m_ammoCounter->posX=64;
	m_ammoCounter->posY=64;
	//Sounds
	jump_sound=initSounds();
	attack_sound=initSounds2();
	walk_sound=initSounds3();
}

Player::~Player() {
	lnSpriteDestroy(m_sprite);
	lnSpriteDestroy(m_healthBarSprite);
	m_instance=NULL;

}

lnSprite* Player::getSprite(){
	return m_sprite;
}

void Player::update() {
	if(invincibleCounter > 0){
		invincibleCounter--;
	}else{
		m_invincible=false;
	}
	if(m_speedUpTimer > 0){
		m_speedUpTimer--;
	}else{	
		m_maxSpeedX=m_prevMaxSpeedX;
		m_maxSpeedY=m_prevMaxSpeedY;
		m_runSpeed=m_prevRunSpeed;
		m_jumpSpeed=m_prevJumpSpeed;
		/*m_prevMaxSpeedX=m_maxSpeedX;
		m_prevMaxSpeedY=m_maxSpeedY;
		m_prevRunSpeed=m_runSpeed;
		m_prevJumpSpeed=m_jumpSpeed;*/
	}
	setPrevBounds();
	
	//input
	updateInput(&touch);
	handleInput(&jump_sound,&attack_sound,&walk_sound,&touch);
	//move the Player
	applyGravity();
	applyFriction();

	//AKS
	//HACK:Uses counter varible to determine
	//length of attack animation
	if(counter > 0){
		counter--;
	}else if(counter == 0){
		m_attacking=false;
	}


	//TODO - maybe put this in its own function?
	if (velocity.x > m_maxSpeedX) {
		velocity.x = m_maxSpeedX;
	} else if (velocity.x < -m_maxSpeedX) {
		velocity.x = -m_maxSpeedX;
	}
	if(velocity.y > m_maxSpeedY){
		velocity.y = m_maxSpeedY;
	}else if(velocity.y < -m_maxSpeedY){
		velocity.y = -m_maxSpeedY;
	}

	if(position.x < 0.0 || position.x > 399.0*16.0){
		position.x = prevPosition.x;
	}
	if(position.y < -3.7*16.0){
	position.y=prevPosition.y;
	}

	if(position.y > 59.0*16.0){
	m_health = 0;
	isDead();
	}


	prevPosition.x = position.x;
	prevPosition.y = position.y;
	position.x += velocity.x;
	position.y += velocity.y;

	if(velocity.y > 1.0){
		if(m_jumpCount == 0){
			m_jumpCount = 3;
		}
		m_inAir=true;
	}
	//Update bounding boxs
	setBounds();

	handleAnimation();

	frames++;
	//printInfo();

	//Display sprite
	display();
}

void Player::handleAnimation(){
	if(m_icy){
		if(!m_attacking){
			m_spriteAnim->animStart = 0;
			m_spriteAnim->animEnd = 5;
			lnSpriteAnimLoop(m_sprite, ANIMTYPE_LOOP,0,1);
		}else{
			m_spriteAnim->animStart = 36;
			m_spriteAnim->animEnd = 37;
			lnSpriteAnimLoop(m_sprite, ANIMTYPE_LOOP,0,1);
		}
	}else if(m_attacking){//attacking animation
		if(m_inAir){
			m_spriteAnim->animStart = 60;
			m_spriteAnim->animEnd = 61;
			lnSpriteAnimLoop(m_sprite, ANIMTYPE_LOOP,0,1);
		}else if(velocity.x != 0.0){
			m_spriteAnim->animStart = 48;
			m_spriteAnim->animEnd = 55;
			lnSpriteAnimLoop(m_sprite, ANIMTYPE_LOOP,0,1);
		}else{
			m_spriteAnim->animStart = 36;
			m_spriteAnim->animEnd = 37;
			lnSpriteAnimLoop(m_sprite, ANIMTYPE_LOOP,0,1);
		}
	}else if(m_inAir){//jumping animation
		m_spriteAnim->animStart = 24;
		m_spriteAnim->animEnd = 28;
		lnSpriteAnimLoop(m_sprite, ANIMTYPE_LOOP, 0, 1);
	}else if(velocity.x != 0.0){//running animation
		m_spriteAnim->animStart = 12;
		m_spriteAnim->animEnd = 20;
		lnSpriteAnimLoop(m_sprite, ANIMTYPE_LOOP, 0, 1);
	}else {//idle animation
		m_spriteAnim->animStart = 0;
		m_spriteAnim->animEnd = 5;
		lnSpriteAnimLoop(m_sprite, ANIMTYPE_LOOP, 0, 1);
	}

	//Make sprite face the direction it is moving in 
	if(m_facingRight){
		m_sprite->hFlip=true;
	}else{
		m_sprite->hFlip=false;
	}

	//Lifebar
	m_healthBarSpriteAnim->animStart=30-(int)m_health/4;
	m_healthBarSpriteAnim->animEnd=30-(int)m_health/4;
	m_healthBarSprite->isHidden=false;
	lnSpriteAnimLoop(m_healthBarSprite,ANIMTYPE_LOOP,0,1);

	//printf("%i\n",m_secondaryAmmo);
	//Ammo Counter
	/*if(m_ammoCounter->isHidden == true){
		printf("hidden");
	}else{
		printf("not hidden");
	}*/
	if(m_secondaryAmmo<21){
		m_ammoCounterAnim->animStart=0;//m_secondaryAmmo;
		m_ammoCounterAnim->animEnd=20;//m_secondaryAmmo;
		m_ammoCounter->isHidden=false;
		lnSpriteAnimLoop(m_ammoCounter,ANIMTYPE_LOOP,0,1);
	}else{
		m_ammoCounterAnim->animStart=21;
		m_ammoCounterAnim->animEnd=21;
		m_ammoCounter->isHidden=false;
		lnSpriteAnimLoop(m_ammoCounter,ANIMTYPE_LOOP,0,1);
	}
	
}

void Player::display(){
	//write the position of the sprite to the screen
	localPos.x = position.x - playerOffset.x;
	localPos.y = position.y - playerOffset.y;
	m_sprite->posX = (int)localPos.x;
	m_sprite->posY = (int)localPos.y;
	//printInfo();
}


void Player::updateInput(touchPosition *touch){
	//call scanKeys in game class now
	//scanKeys();
	*touch = touchReadXY();
}

void Player::handleInput(TransferSoundData * soundData,TransferSoundData * soundData2,TransferSoundData * soundData3, touchPosition *touch){
	if((keysDown() & KEY_A)){// && (!m_inAir)){
		jump();
		playSound(soundData);
	}

	if((keysDown() & KEY_B) || (keysHeld() & KEY_B)){
		if((keysDown() & KEY_DOWN) || (keysHeld() & KEY_DOWN)){
			m_attackDirection=1;
		}else if((keysDown() & KEY_UP) || (keysHeld() & KEY_UP)){
			m_attackDirection=3;
		}else{
			if(m_facingRight){
				m_attackDirection=0;
			}else{
				m_attackDirection=2;
			}
		}
		
		
		if(!m_attacking){
			attack();
			playSound(soundData2);
		}
	}

	if((keysDown() & KEY_X) || (keysHeld() & KEY_X)){
		attack2();
		if(!m_attacking){
			playSound(soundData2);
		}
	}

	if((keysDown() & KEY_LEFT) || (keysHeld() & KEY_LEFT)){
		move(0);
		if(!m_inAir){
			playSound(soundData3);
		}
	}else if((keysDown() & KEY_RIGHT) || (keysHeld() & KEY_RIGHT)){
		move(1);
		if(!m_inAir){
			playSound(soundData3);
		}
	}
}

void Player::setHidden(bool hidden){
	m_sprite->isHidden=hidden;
	m_healthBarSprite->isHidden=hidden;
}

void Player::reset(){
	velocity.x=0;
	velocity.y=0;

	m_maxSpeedX = 2.0;
	m_maxSpeedY = 7.0;
	m_runSpeed = 0.5;
	m_jumpSpeed = m_prevJumpSpeed;//was 6.5
	m_friction = .8;
	m_jumpCount = 0;
	/*m_prevMaxSpeedX=m_maxSpeedX;
	m_prevMaxSpeedY=m_maxSpeedY;
	m_prevRunSpeed=m_runSpeed;
	m_prevJumpSpeed=m_jumpSpeed;
	m_prevFriction = m_friction;*/
	m_gravityConstant = 0.25;
	m_facingRight = false;
	m_inAir = true;
	m_attacking = false;
	m_icy=false;

	setPrevBounds();
	setBounds();

	m_secondaryAmmo=10;
	m_health=120;
	m_speedUpTimer=0;
}
void Player::move(int direction) {
	if (direction == 1) {
	   //move right
	   velocity.x += m_runSpeed;
	  m_facingRight = true;
	} else if (direction == 0) {
	   //move left
	   velocity.x -= m_runSpeed;
	   m_facingRight = false;}
}

void Player::jump() {
	if(m_jumpCount < 2){
		velocity.y = -m_jumpSpeed;
		scorekeeper* Score = scorekeeper::getInstance();
		Score->increaseJumps();
	}
	m_jumpCount++;
	m_inAir = true;
}

void Player::attack(){
	//AKS
	if(!m_attacking){
		m_attacking=true;
		counter = 15; //was 60
		generateBullet();
	}
}

void Player::attack2(){
	if(m_secondaryAmmo != 0){
		if(!m_attacking){
			m_secondaryAmmo--;
			m_attacking=true;
			counter = 60;
			generateBullet2();
		}
	}
}

void Player::generateBullet(){
	scorekeeper* Score = scorekeeper::getInstance();
	Score->increaseShots();
	game* Game = game::getInstance();
	weapon *temp = new weapon();
	temp->createWeaponSprite(megaman3Tiles,megaman3Pal,32,38,38,10,12);
	temp->setPlayerOwned(true);
	temp->setHidden(false);
	temp->setDamage(10);
	temp->markedForDeletion=false;
	temp->setOffset(playerOffset.x,playerOffset.y);
	//printf("%i\n",m_attackDirection);
	if(m_attackDirection==0){
		temp->setPos(position.x+32.0,position.y+8.0);
		temp->move(1);
	}else if(m_attackDirection==1){
		temp->setPos(position.x+8.0,position.y+32.0);
		temp->move(2);
		
	}else if(m_attackDirection==2){
		temp->setPos(position.x-32.0,position.y+8.0);
		temp->move(0);
	}else{
		temp->setPos(position.x+8.0,position.y-32.0);
		temp->move(3);
	}

	Game->projectiles.push_back(temp);
	//lnSpriteSetPalette(Game->enemies[16]->e_sprite, enemy1Pal, COLMODE_16, true);
}

void Player::generateBullet2(){
	scorekeeper* Score = scorekeeper::getInstance();
	Score->increaseShots();
	game* Game = game::getInstance();
	weapon *temp = new weapon();
	temp->createWeaponSprite(megaman3Tiles,megaman3Pal,32,40,40,0,32);
	temp->setPlayerOwned(true);
	temp->setDamage(15);
	temp->setHidden(false);
	temp->markedForDeletion=false;
	temp->setOffset(playerOffset.x,playerOffset.y);

	if(m_facingRight){
		temp->setPos(position.x,position.y);
		temp->move(1);
	}else{
		temp->setPos(position.x-32.0,position.y);
		temp->move(0);
	}
	Game->projectiles.push_back(temp);
}


void Player::playerDidHit(int index){
	game* Game = game::getInstance();
	Game->projectiles.at(index)->destroyWeaponSprite();
	Game->projectiles.erase(Game->projectiles.begin()+index);
}

void Player::setWeapon(weapon newWeapon){
	m_curWeapon = newWeapon;
	m_curWeapon.setHidden(true);
}

void Player::setWeapon2(weapon newWeapon){
	m_secondWeapon = newWeapon;
	m_secondWeapon.setHidden(true);
}

void Player::addAmmo(){
	m_secondaryAmmo += 5;
}

void Player::healthPack(){
	m_health = 120;
}

void Player::tookDamage(int damage){
	if(!m_invincible){
		m_health -= damage;
		if(m_health < 0){
			m_health = 0;
		}
	}
}

bool Player::isDead(){
	if(m_health == 0){
		return true;
	}else{
		return false;
	}
}

void Player::setPos(tFloat posX,tFloat posY){
	position.x=posX;
	position.y=posY;

	setBounds();
	display();
	//printInfo();
}


void Player::setOffset(tFloat offsetX, tFloat offsetY){
	playerOffset.x=offsetX;
	playerOffset.y=offsetY;
}

void Player::setBounds(){
	float spriteOffsetX = 6.0;
	//float spriteOffsetY = 0.0;
	float spriteSize = 20.0;
	m_boundingBox.top = position.y;
	m_boundingBox.bottom = position.y + 31.0;
	m_boundingBox.left = (Fixed)spriteOffsetX + position.x;
	m_boundingBox.right = (Fixed)spriteOffsetX + position.x + spriteSize;
}

void Player::setPrevBounds(){
	float spriteOffsetX = 6.0;
	//float spriteOffsetY = 0.0;
	float spriteSize = 20.0;
	m_prevBoundingBox.top = position.y;
	m_prevBoundingBox.bottom = position.y + 31.0;
	m_prevBoundingBox.left = (Fixed)spriteOffsetX + position.x;
	m_prevBoundingBox.right = (Fixed)spriteOffsetX + position.x + spriteSize;

}
void Player::applyGravity() {
	//if(m_inAir){
		velocity.y += m_gravityConstant;
	//}
}
void Player::applyFriction() {
	if(!m_inAir){
		if(m_icy){
			velocity.x *= 1.0;
		}else{
			velocity.x *= m_friction;
		}
	}
	if((velocity.x < 0.1) && (velocity.x > -0.1)){
		velocity.x = 0.0;
	}
}


bool Player::checkCollide(BoundingBox target){
	if(m_boundingBox.left > target.right || target.left > m_boundingBox.right){
		return false;  //No collision
	}
	if(m_boundingBox.top > target.bottom || target.top > m_boundingBox.bottom){
		return false;  //No collision
	}
	return true;  //Collision
}

bool Player::checkCollideXprev(BoundingBox target){
	if(m_prevBoundingBox.left > target.right ||
		m_prevBoundingBox.right < target.left){
			return false;
	}
	return true;
}

bool Player::checkCollideYprev(BoundingBox target){
	if(m_prevBoundingBox.top >= target.bottom ||
		m_prevBoundingBox.bottom <= target.top){
			return false;
	}
	return true;
}

void Player::requestFullStop(){

	velocity.x = 0;
}

void Player::requestHover(){
	m_inAir=false;
	velocity.y =0;
	m_jumpCount=0;
}

void Player::requestFall(){
	//AKS
	//Not used currently
	velocity.y=0;
}

void Player::hitGround(){
	//not used
	velocity.y=0;
}

void Player::reverse(){
	if(!m_invincible){
		m_invincible=true;
		invincibleCounter = 1*60;
	}
	/*if(velocity.x < 0.0){
	velocity.x = 5.0;
	}else{
	velocity.x = -5.0;
	}
	if(velocity.y > 0.2){
	velocity.y = -4.5;
	}*/
	
}

t2DVector Player::getPosition() {
	return position;
} 



t2DVector Player::getPositionPrev(){
	return prevPosition;
}

t2DVector Player::getOffset(){
	return playerOffset;
}

t2DVector Player::getVelocity(){
	return velocity;
}

BoundingBox Player::getBounds(){
	return m_boundingBox;
}

BoundingBox Player::getPrevBounds(){
	return m_prevBoundingBox;
}

TransferSoundData Player::initSounds(){
	TransferSoundData * jump_sound = new TransferSoundData();
	jump_sound->data = jump_1_22050_8_s_raw;
	jump_sound->len = jump_1_22050_8_s_raw_size;
	jump_sound->rate = 22050;
	jump_sound->vol = 127;
	jump_sound->pan = 64;
	jump_sound->format = 1;
	return *jump_sound;
}

TransferSoundData Player::initSounds2(){
	TransferSoundData * attack_sound = new TransferSoundData();
	attack_sound->data = blaster_1_22050_8_s_raw;
	attack_sound->len = blaster_1_22050_8_s_raw_size;
	attack_sound->rate = 22050;
	attack_sound->vol = 127;
	attack_sound->pan = 64;
	attack_sound->format =1;
	return *attack_sound;
}

TransferSoundData Player::initSounds3(){
	TransferSoundData * walk_sound = new TransferSoundData();
	walk_sound->data = footsteps_1_22050_8_s_raw;
	walk_sound->len = footsteps_1_22050_8_s_raw_size;
	walk_sound->rate = 22050;
	walk_sound->vol = 127;
	walk_sound->pan = 64;
	walk_sound->format =1;
	return *walk_sound;
}

void Player::printInfo(t2DVector camera){
	//printf("\x1b[2J");
	scorekeeper *Score = scorekeeper::getInstance();
	consoleClear();
	printf("\n\n\n\n\n\n\n");
	printf("    Ammo Left: %i\n",m_secondaryAmmo);
	printf("    Score: %i\n",Score->getScore());
	/*printf("    Player X-Cord: %03.2f\n",(float)position.x);
	printf("    Player Y-Cord: %03.2f\n",(float)position.y);
	printf("    Player X-Velocity:  %02.2f\n",(float)velocity.x);
	printf("    Player Y-Velocity:  %02.2f\n",(float)velocity.y);
	printf("    Gametime: %i\n",frames/60);
	printf("    Top:    %03.2f %03.2f\n",(float)m_boundingBox.top,(float)m_prevBoundingBox.top);
	printf("    Bottom: %03.2f %03.2f\n",(float)m_boundingBox.bottom,(float)m_prevBoundingBox.bottom);
	printf("    Left:   %03.2f %03.2f\n",(float)m_boundingBox.left,(float)m_prevBoundingBox.left);
	printf("    Right:  %03.2f %03.2f\n",(float)m_boundingBox.right,(float)m_prevBoundingBox.right);
	printf("    Camera: %03.2f %03.2f\n",(float)camera.x,(float)camera.y);
	*/
	//printf("SpeedUp: %i\n",m_speedUpTimer);
}

void Player::speedUp(){
	m_speedUpTimer = 20*60;
	/*m_prevMaxSpeedX = m_maxSpeedX;
	m_prevMaxSpeedY = m_maxSpeedY;
	m_prevRunSpeed = m_runSpeed;
	m_prevJumpSpeed = m_jumpSpeed;*/
	
	//AKS
	//TODO: Try different speeds
	m_maxSpeedX *=1.4;
	m_maxSpeedY +=0.9;
	m_runSpeed +=1;
	m_jumpSpeed *=1.25;
}

void Player::lessFriction(bool isThere){
	m_icy=isThere;
}

void Player::knockback(int direction){
	if(direction == 0){
		velocity.x= 4.0;
		velocity.y= -2.0;
	}else{
		velocity.x= -4.0;
		velocity.y= -2.0;
	}
}
